# %%
import torch
import numpy as np

# %%
# 原始数据直接生成张量，喂入tensor就好了
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)

# %%
# from_numpy将numpy数组转换为tensor
np_array = np.array(data)
x_np = torch.from_numpy(np_array)

# %%
# 通过已有的张量来生成张量，会保留shape和dtype
# 当然也可以重新指定dtype
x_ones = torch.ones_like(x_data)
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float)
print(f"Random Tensor: \n {x_rand} \n")

# %%
# 通过指定数据维度来生成
shape = (2, 3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")

# %%
# 张量的属性：1形状，2数据类型，3在CPU/GPU上
tensor = torch.rand(3, 4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")

# %%
# 看下cuda的环境是否可以使用
if torch.cuda.is_available():
    tensor = tensor.to('cuda')
# %%
# 张量的索引和切片运算
tensor = torch.ones(4, 4)
tensor[:, 1] = 0
print(tensor)

# %%
# 张量的拼接
# torch.stack 也可以实现这个功能
# 需要注意说：dim所在的那一维可以不同，但是除了dim其他维度一定要相同
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)

torch.cat([torch.ones(1, 2, 1), torch.ones(111, 2, 1)], dim=0)

# %%
# tensor.mul 和 * 是同一个意思都是矩阵乘法，逐元素乘法然后相加，内积！
print(f"tensor.mul(tensor) \n {tensor.mul(tensor)} \n")
print(f"tensor * tensor \n {tensor * tensor}")

# tensor.matmul 和 tensor.mm 和 @是同一个意思，是矩阵乘法！
print(f"tensor.matmul(tensor.T) \n {tensor.matmul(tensor.T)} \n")
print(f"tensor @ tensor.T \n {tensor @ tensor.T}")
# 举例子：
a1 = torch.ones(1, 2)
a2 = torch.ones(1, 2)
b = torch.ones(2, 1)
# 显然a1,a2可以做内积，而a1和b可以做矩阵乘法，而a2和b什么都不能做
print(a1 * a2)
print(a1 @ b)

# %%
# 原地操作，带_也就是说，运算结果会被复制到最右边
print(tensor, "\n")
tensor.add_(5)
print(tensor)

# %%
# tensor和numpy之间的转换
# tensor.numpy()将tensor转换为numpy
t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
# torch.from_numpy()将numpy转换为tensor
n = np.ones(5)
t = torch.from_numpy(n)
print(t)
# %%
# 这个是将运算结果复制出去给n
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
